package com.xuecheng.content.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.CourseBaseDTO;
import com.xuecheng.api.content.model.qo.QueryCourseModel;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.page.PageRequestParams;
import com.xuecheng.common.domain.page.PageVO;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseChargeEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.controller.CourseAuditController;
import com.xuecheng.content.convert.CourseBaseConverrt;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.CourseMarket;
import com.xuecheng.content.mapper.CourseBaseMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.CourseMarketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
/**
 * <p>
 * 课程基本信息 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2022-06-14
 */
@Service
public class CourseBaseServiceImpl extends ServiceImpl<CourseBaseMapper, CourseBase> implements CourseBaseService {

    @Autowired
    private CourseMarketService courseMarketService;

    /**
     *  功能实现：
     *      0.是否开启事务
     *      1.判断数据
     *      2.构建分页对象page
     *      3.构架你查询对象QueryWrapper
     *      4.调用api获得结果
     *      5.获得结果数据封装对象并返回
     *
     * @param params - PageRequestParams 分页查询对象
     * @param model - QueryCourseModel 课程条件封装对象
     * @return
     */
    public PageVO<CourseBaseDTO> queryCourseList(PageRequestParams params, QueryCourseModel model,Long companyId) {

        // 1.判断数据
        if(params.getPageNo() < 1){
            params.setPageNo(PageRequestParams.DEFAULT_PAGE_NUM);
        }
        if(params.getPageSize() < 1){
            params.setPageSize(PageRequestParams.DEFAULT_PAGE_SIZE);
        }

        // 2.构建分页对象page
        Page<CourseBase> page = new Page<>(params.getPageNo(), params.getPageSize());

        // 3.构架你查询对象QueryWrapper
        //elect * from course_base where audit_status = ? and course_name like ? and company_id = ?
        LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>();

        //完整版
//        if(!(StringUtils.isEmpty(model.getAuditStatus())))        {
//            queryWrapper.eq(CourseBase::getAuditStatus,model.getAuditStatus());
//        }
//
//        if(!(StringUtils.isEmpty(model.getCourseName()))){
//            queryWrapper.like(CourseBase::getName,model.getCourseName());
//        }

        // 简化版
        queryWrapper.eq(!(StringUtils.isEmpty(model.getAuditStatus())),CourseBase::getAuditStatus, model.getAuditStatus());
        queryWrapper.like(!(StringUtils.isEmpty(model.getCourseName())),CourseBase::getName, model.getCourseName());

        if (!(ObjectUtils.nullSafeEquals(companyId, CourseAuditController.OPERATION_FLAG))) {
            queryWrapper.eq(CourseBase::getCompanyId, companyId);
        }


        queryWrapper.eq(CourseBase::getStatus,CommonEnum.USING_FLAG.getCodeInt());


        queryWrapper.eq(CourseBase::getCompanyId,companyId);

        queryWrapper.orderByDesc(CourseBase::getCreateDate);


        // 4.调用api获得结果
        Page<CourseBase> pageResult = this.page(page, queryWrapper);

        List<CourseBase> records = pageResult.getRecords();

        long total = pageResult.getTotal();


        List<CourseBaseDTO> dtos= Collections.EMPTY_LIST;
        //将po集合数据转为dto集合数据
        if(!(CollectionUtils.isEmpty(records))){
            dtos= CourseBaseConverrt.INSTANCE.entitys2dtos(records);
        }

        // 5.获得结果数据封装对象并返回
        PageVO<CourseBaseDTO> pageVO = new PageVO<>(dtos, total, params.getPageNo(), params.getPageSize());


        return pageVO;
    }


    /**
     *   业务分析：
     *      0. 是否开启事务
     *          增删改操作比加事务注解：@Tnalransactiol
     *      1. 判断关键数据
     *          数据的关键性，如果没有，无法操作内容
     *              数据判断的依据:
     *                  1.看前端页面，只要对象属性带红星的就代表数据必填
     *                  2.节后文档，必须为必填项
     *                  3.数据库表结构
     *                      有非空的字段
     *                          companyid  name  mt  st  grade  teachermode  auditstatus
     *
     *           判断课程营销模式：
     *              如果课程是收费课程，必须要改价格
     *      2.有些数据内容不是前端维护，由后端来维护
     *          id  create-date  change-date  status  audit_status
     *
     *      3.添加数据到数据库
     *          课程基础信息表  course_base（先保存课程基础信息表）
     *          课程营销表  course_market（要关联course_id）
     *
     *          对操作数据库的结果进行判断
     *
     *      4.返回操作后的结果数据dto
     *          course_base
     *          course_market
     *
     *
     *
     * @param dto   CourseBaseDTO  课程基础信息dto封装数据
     * @return
     */
    @Transactional
    public CourseBaseDTO createCourse(CourseBaseDTO dto) {

        String charge = verifyCourseMsg(dto);


        // 2.有些数据内容不是前端维护，由后端来维护
         //     id(数据库自动生成)  create-date(mp)  change-date(mp)
        //     status(数据库自动填充)
        //     audit_status(后端业务自己维护)--对于新增课程的状态-未提交
        dto.setAuditStatus(CourseAuditEnum.AUDIT_UNPAST_STATUS.getCode());


        //将dto数据转换为po数据
        CourseBase courseBase = CourseBaseConverrt.INSTANCE.dto2entity(dto);


        // 3.添加数据到数据库
         //     课程基础信息表  course_base（先保存课程基础信息表）
        courseBase.setId(null);

        boolean baseResult = this.save(courseBase);

        if(!baseResult){
            throw new RuntimeException("课程基础信息保存失败");
        }


        //mp会对添加后的数据id进行回调
        Long courseBaseId = courseBase.getId();

        //课程营销表  course_market（要关联course_id）
         //     对操作数据库的结果进行判断
        CourseMarket courseMarket = new CourseMarket();
        courseMarket.setCourseId(courseBaseId);
        courseMarket.setCharge(charge);

        BigDecimal price=dto.getPrice();
        //两个数据类型不一致，需要转换一下
        courseMarket.setPrice(price.floatValue());

        boolean marketResult = courseMarketService.save(courseMarket);

        if(!marketResult){
            throw new RuntimeException("课程营销数据保存失败");
        }



         // 4.返回操作后的结果数据dto
         //   course_base
         //   course_market
        //获得数据的最新内容，需要查询
        CourseBase po = this.getById(courseBaseId);

        CourseBaseDTO resultDto = CourseBaseConverrt.INSTANCE.entity2dto(po);

        resultDto.setCharge(dto.getCharge());
        resultDto.setPrice(dto.getPrice());

        return resultDto;


    }

    private String verifyCourseMsg(CourseBaseDTO dto) {
        // 1. 判断关键数据（如果不满足数据要求，就返回错误数据）
        //               数据的关键性，如果没有，无法操作内容
        //                   数据判断的依据:
        //            1.看前端页面，只要对象属性带红星的就代表数据必填
        //                       2.节后文档，必须为必填项
        //                       3.数据库表结构
        //                           有非空的字段
        //                               companyid  name  mt  st  grade  teachermode

        if (ObjectUtils.isEmpty(dto.getCompanyId())) {
            // 业务异常：程序员在做业务判断时，数据有问题。
            // 异常：
            //    1.终止程序
            //    2.传递错误信息
            //    3.使用运行时异常来抛出
            //        运行时异常不需在编译期间处理
            throw new RuntimeException("公司id不能为空");
        }

        if (StringUtil.isBlank(dto.getName())) {
            throw new RuntimeException("课程名称不能为空");
        }

        if (StringUtil.isBlank(dto.getMt())) {
            throw new RuntimeException("课程大分类不能为空");
        }

        if (StringUtil.isBlank(dto.getSt())) {
            throw new RuntimeException("课程小分类不能为空");
        }

        if (StringUtil.isBlank(dto.getGrade())) {
            throw new RuntimeException("课程等级不能为空");
        }

        if (StringUtil.isBlank(dto.getTeachmode())) {
            throw new RuntimeException("课程教学模式不能为空");
        }

        if (StringUtil.isBlank(dto.getUsers())) {
            throw new RuntimeException("使用人群不能为空");
        }

        if (StringUtil.isBlank(dto.getCharge())) {
            throw new RuntimeException("课程收费不能为空");
        }


        //          判断课程的营销模式
        //              如果课程是收费，必须要该价格
        //获取收费模式
        String charge= dto.getCharge();
        //获取收费的代码
        String code= CourseChargeEnum.CHARGE_YES.getCode();
        //判断charge和code是否相等
        if(ObjectUtils.nullSafeEquals(charge,code)){

            BigDecimal price = dto.getPrice();
            if(ObjectUtils.isEmpty(price)){
                throw  new RuntimeException("收费课程价格不能为空");

            }
        }else{
            //免费课程的价格默认赋值为0或null
            dto.setPrice(new BigDecimal("0.0"));
        }
        return charge;
    }

    /*
    * 业务分析：
    *   1.判断关键数据：前端传来的数据，需要校验的内容
    *       courseBaseId， companyId
    *
    *   2.判断业务数据：根据关键数据来操作的数据内存，在后端存储
    *       判断课程信息是否存在
    *           根据id来查询
    *       判断是否是同一家机构
    *           将数据库中的CourseBaseliCompanyId和前端传来的CompanyId进行对比
    *       判断课程数据是否删除
    *           status：1-使用     0-删除
    *
    *   3.将po数据转换为dto并返回
    *       courseBase
    *       courseMarket
    *
    * */
    public CourseBaseDTO getCourseById(Long courseBaseId, Long companyId) {

        // 1.判断关键数据：前端传来的数据，需要校验的内容
        //   courseBaseId,  companyId
        if(ObjectUtils.isEmpty(courseBaseId) || ObjectUtils.isEmpty(companyId)){
            throw new RuntimeException("传入的参数和接口不匹配");
        }

        // 2.判断业务数据：根据关键数据来操作的数据内容，在后端存储
        //   判断课程信息是否存在
        //      根据id来查询
        //   判断是否是同一家机构
        //      将数据库中的CourseBase里Companyid和前端传来的Companyid进行对比
        CourseBase courseBase = getCourseBaseByBaseId(courseBaseId, companyId);

        //3.将po数据转为dto并返回
        //  courseBase
        //  courseMarket
        CourseBaseDTO courseBaseDTO = CourseBaseConverrt.INSTANCE.entity2dto(courseBase);
        //课程营销
        CourseMarket courseMarket = getCourseMarketByCourseId(courseBaseId);

        courseBaseDTO.setCharge(courseMarket.getCharge());
        courseBaseDTO.setPrice(new BigDecimal(courseMarket.getPrice().toString()));

        return courseBaseDTO;


    }




    /*
    * 业务分析：
    *   0.是否开启事务
    *   1.判断关键数据
    *       可以直接调用添加对关键数据的判断
    *       判断课程的id不能为空
    *   2.判断业务数据
    *       课程基础信息
    *           判断是否存在
    *           判断是否是同一家机构
    *           判断是否删除
    *           判断课程审核状态
    *               未提交和审核未通过才可以进行修改
    *       课程营销（根据courseid）
    *
    *   3.修改数据
    *       修改课程基础信息数据
    *       修改课程营销数据
    *           如果是收费课程，直接覆盖源数据（charge，price）
    *           如果是免费课程，直接覆盖源数据（charge，price=0.0）
    *
    *   4.将数据库最新数据返回给前端
    *       coursebase
    *       coursemarket
    * */
    @Transactional   //开启事务
    public CourseBaseDTO modifyCourseById(CourseBaseDTO dto) {
        //1.判断关键数据
        //  可以直接调用添加时对关键数据的判断
        //  判断课程的id不能为空
        verifyCourseMsg(dto);

        Long courseBaseId = dto.getCourseBaseId();
        if (ObjectUtils.isEmpty(courseBaseId)) {
            throw new RuntimeException("修改课程id不能为空");
        }


        // 2.判断业务数据
        //   课程基础信息
        //     判断是否存在
        //     判断是否是同一家教学机构
        //     判断是否删除
        //     判断课程审核状态
        //        未提交和审核未通过才可以进行修改
        //   课程营销（根据courseid）
        getCourseByLogic(dto.getCompanyId(), courseBaseId);

        //     课程营销
        CourseMarket courseMarket = getCourseMarketByCourseId(courseBaseId);


        // 将dto转为po
        //     课程基础信息表
        //         有些内容是不容修改的：companyid、auditstatus、status
        CourseBase po = CourseBaseConverrt.INSTANCE.dto2entity(dto);


        // 3.修改数据
        //   修改课程基础信息数据
        //   修改课程营销数据
        //     如果是收费课程，直接覆盖源数据（charge、price）
        //     如果是免费课程，直接覆盖源数据（charge、price=0.0）
        po.setCompanyId(null);
        po.setAuditStatus(null);
        po.setStatus(null);

        boolean baseResult = this.updateById(po);

        if (!baseResult) {
            throw new RuntimeException("课程信息修改失败");
        }

        //     课程营销数据表
        //         charge price
        // 如果使用mq修改一张表中的极个别数据，UpdateWrapper
        //update course_market set charge=xxx,price=xxx where courseid=xx
        LambdaUpdateWrapper<CourseMarket> marketUpdateWrapper = new LambdaUpdateWrapper<>();
        marketUpdateWrapper.set(CourseMarket::getCharge,dto.getCharge());

        String charge=dto.getCharge();
        if(CourseChargeEnum.CHARGE_YES.getCode().equals(charge)){   //如果课程收费

            BigDecimal price=dto.getPrice();
            if(ObjectUtils.isEmpty(price)){
                throw new RuntimeException("收费课程价格不能为空");
            }

            marketUpdateWrapper.set(CourseMarket::getPrice,dto.getPrice().floatValue());
        }else {
            //如果课程免费
            marketUpdateWrapper.set(CourseMarket::getPrice,0F);
        }
        marketUpdateWrapper.eq(CourseMarket::getCourseId,courseBaseId);

        boolean marketResult = courseMarketService.update(marketUpdateWrapper);

        //修改数据是要判断修改后的结果
        if(!marketResult){
            throw new RuntimeException("修改课程营销数据失败");
        }

        //4.将修改后的最新数据返回给前端
        CourseBaseDTO resultDTO = getLastCourseBaseDTO(dto, courseBaseId);

        return resultDTO;

    }

    /*
    * 业务分析：
    *   1.是否要开启事务
    *       需要开启
    *   2.判断关键数据
    *       courseBaseId
    *       companyId
    *   3.判断业务数据
    *       判断课程是否存在
    *       判断是否是同一家机构
    *       判断课程是否已经删除
    *       判断课程的审核状态
    *           为提交
    *           审核为通过
    *           只有这些状态才能被删除
    *    4.删除课程基础信息（根据id删除）
    *       修改数据库的status
    *
    * */
    @Transactional   //开启事务
    public void removeCourseBase(Long courseBaseId, Long companyId) {

        // 2.判断关键数据
        //      courseBaseId
        //      companyid
        if(ObjectUtils.isEmpty(courseBaseId) || ObjectUtils.isEmpty(companyId)){
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }


        //3.判断业务数据
        //     判断课程是否存在
        //     判断是否是同一家机构
        //     判断课程是否已经删除
        //     判断课程的审核状态
        //         未提交
        //         审核未通过
        //         只有这些状态才可以被删除
        getCourseByLogic(companyId,courseBaseId);


        // 4.删除课程基础信息（根据id删除）
        //     修改数据库的数据status
        // update course_base set status = ? where course_id = ?
        LambdaUpdateWrapper<CourseBase> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(CourseBase::getStatus,CommonEnum.DELETE_FLAG.getCodeInt());
        updateWrapper.set(CourseBase::getChangeDate, LocalDateTime.now());

        updateWrapper.eq(CourseBase::getId,courseBaseId);

        boolean result = update(updateWrapper);
        if(!result){
            ExceptionCast.cast(ContentErrorCode.E_120017);
        }

    }




    private void getCourseByLogic(Long companyId, Long courseBaseId) {

        CourseBase courseBase = getCourseBaseByBaseId(courseBaseId, companyId);

        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
        ) {
            throw new RuntimeException("课程审核状态异常");
        }
    }



    private CourseBase getCourseBaseByBaseId(Long courseBaseId, Long companyId) {
        // select * from course_base where id = ? and companyId = ?
        LambdaQueryWrapper<CourseBase> baseQueryWrapper = new LambdaQueryWrapper<>();
        baseQueryWrapper.eq(CourseBase::getId, courseBaseId);
        baseQueryWrapper.eq(CourseBase::getCompanyId, companyId);
        // baseQueryWrapper.eq(CourseBase::getStatus, CommonEnum.USING_FLAG.getCodeInt());

        CourseBase courseBase = this.getOne(baseQueryWrapper);

        if (ObjectUtils.isEmpty(courseBase)) {
            throw new RuntimeException("课程不存在");
        }

        Integer status = courseBase.getStatus();
        if (!(CommonEnum.USING_FLAG.getCodeInt().equals(status))) {
            throw new RuntimeException("课程信息已经被删除");
        }
        return courseBase;
    }

    private CourseMarket getCourseMarketByCourseId(Long courseBaseId) {
        LambdaQueryWrapper<CourseMarket> marketQueryWrapper = new LambdaQueryWrapper<>();
        marketQueryWrapper.eq(CourseMarket::getCourseId, courseBaseId);

        CourseMarket courseMarket = courseMarketService.getOne(marketQueryWrapper);

        if (ObjectUtils.isEmpty(courseMarket)) {
            throw new RuntimeException("课程营销数据不存在");
        }
        return courseMarket;
    }

    private CourseBaseDTO getLastCourseBaseDTO(CourseBaseDTO dto,Long id){
        CourseBase po = this.getById(id);

        CourseBaseDTO resultDTO = CourseBaseConverrt.INSTANCE.entity2dto(po);
        resultDTO.setPrice(dto.getPrice());
        resultDTO.setCharge(dto.getCharge());
        return resultDTO;
    }


    /*
     * 业务分析：
     *   0.是否开启事务
     *   1.判断关键数据
     *   2.判断业务数据
     *      课程基础信息
     *         判断是否存在
     *         判断是否是同一家机构
     *         判断是否删除
     *         判断审核状态：未提交、审核未通过
     *   3.修改课程的审核状态：已提交
     * */
    public void commitCourseById(Long courseBaseId, Long companyId) {
        // 1.判断关键数据
        if (ObjectUtils.isEmpty(courseBaseId)||
                ObjectUtils.isEmpty(companyId)
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }


        // 2.判断业务数据
        //    课程基础信息
        //       判断是否存在
        //       判断是否是同一家机构
        //       判断是否删除
        //       判断审核状态：未提交、审核未通过
        getCourseByLogic(courseBaseId,companyId);

        // 3.修改课程的审核状态：已提交
        // update course_base set audit_status = ? where id = ?
        LambdaUpdateWrapper<CourseBase> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(CourseBase::getAuditStatus,CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode());
        //只要用了UpdateWrapper，自动填充就失效了.所以得再加一个字段
        updateWrapper.set(CourseBase::getChangeDate,LocalDateTime.now());
        updateWrapper.eq(CourseBase::getId,courseBaseId);

        boolean result = this.update(updateWrapper);

        if(!result){
            ExceptionCast.cast(ContentErrorCode.E_120023);
        }

    }



    /*
     * 业务分析：
     *   1.判断关键数据
     *       auditMind  auditStatus  courseid
     *
     *   2.判断业务数据
     *       课程基础信息
     *           判断是否存在
     *           判断是否删除
     *           判断审核状态
     *               必须为：已提交
     *       审核状态
     *           运营平台只能给课程审核状态赋值：审核通过、审核未通过
     *   3.修改课程审核信息
     *       auditMind  auditStatus  auditNum（每次+1）
     * */
    @Transactional
    public void approveCourse(CourseBaseDTO dto) {

        // 1.判断关键数据
        //      auditMind  auditStatus  courseid
        if (StringUtil.isBlank(dto.getAuditMind())||
                StringUtil.isBlank(dto.getAuditStatus())||
                ObjectUtils.isEmpty(dto.getCourseBaseId())
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }


        //  2.判断业务数据
        //      课程基础信息
        //          判断是否存在
        //          判断是否删除
        //          判断审核状态
        //              必须为：已提交
        LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CourseBase::getId, dto.getCourseBaseId());
        queryWrapper.eq(CourseBase::getStatus, CommonEnum.USING_FLAG.getCodeInt());
        queryWrapper.eq(CourseBase::getAuditStatus, CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode());

        int count = this.count(queryWrapper);

        if (count < 1) {
            ExceptionCast.cast(ContentErrorCode.E_120023);
        }


        //      审核状态
        //          运营平台只能给课程审核状态赋值：审核通过、审核未通过
        String auditStatus = dto.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_UNPAST_STATUS.getCode().equals(auditStatus)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120016);
        }



        //  3.修改课程审核信息
        //      auditMind  auditStatus  auditNum（每次+1）
        LambdaUpdateWrapper<CourseBase> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(CourseBase::getAuditStatus, dto.getAuditStatus());
        updateWrapper.set(CourseBase::getAuditMind, dto.getAuditMind());

        updateWrapper.set(CourseBase::getChangeDate, LocalDateTime.now());
        updateWrapper.eq(CourseBase::getId, dto.getCourseBaseId());

        boolean result = this.update(updateWrapper);

        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120017);
        }

    }

}
